// 打包前优化 generateSubPackageModulesConfig
// 在 uniBuild 任务之前执行
const {series, task, src, parallel, dest} = require('gulp')
const tap = require('gulp-tap')
const path = require('path')
const fs = require('fs')
const {extractRelativePaths} = require('./utils')

// 项目根目录
const rootPath = path.join(__dirname, '../')
const srcPath = path.join(rootPath, './src')
const componentsPath = path.join(rootPath, './src/components')
const pagesPath = path.join(rootPath, './src/pages')
const dependenciesJsonDistPath = path.join(__dirname, './dependencies.json')

// 组件引用根路径
const componentRootPath = '@/components' // 替换为 pages 页面中引入组件的根路径
const newComponentDir = 'componentsauto' // 迁移后的新组件目录

// 页面配置
let pages = {}

// 分包页面路径列表
let subPackagePagePathList = []

// 主包页面路径列表
let mainPackagePagePathList = []

// 组件依赖信息
let componentsMap = {}

const taskMap = {}
const changeFileMap = {}
const deleteFileMap = {}

function initData() {
  // pages.json 会被之前的 gulp 流程修改，因此在执行到当前任务时再获取
  pages = require('../src/pages.json')

  subPackagePagePathList = pages.subPackages.map(item => item.root.replace('pages/', ''))

  mainPackagePagePathList = pages.pages.map(item => {
    const pathParts = item.path.split('/')

    return pathParts.join(`\\${path.sep}`)
  })
}

/**
 * 组件信息初始化
 */
function initComponentsMap() {
  // 为所有 src/components 中的组件创建信息
  return src([`${srcPath}/@(components)/**/**.vue`]).pipe(
    tap(file => {
      let filePath = transferFilePathToComponentPath(file.path)

      componentsMap[filePath] = {
        refers: [], // 引用此组件的页面/组件
        quotes: [], // 此组件引用的组件
        referForMainPackage: false // 是否被主包引用，被主包引用时不需要 copy 到分包
      }
    })
  )
}

/**
 * 分析依赖
 */
function analyseDependencies() {
  return src([`${srcPath}/@(components|pages)/**/**.vue`]).pipe(
    tap(file => {
      // 是否为主包页面
      const isMainPackagePageByPath = checkIsMainPackagePageByPath(file.path)

      // 分析页面引用了哪些组件
      const componentsPaths = Object.keys(componentsMap)
      const content = String(file.contents)

      componentsPaths.forEach(componentPath => {
        if (content.includes(componentPath)) {
          // 当前页面引用了这个组件
          componentsMap[componentPath].refers.push(file.path)

          if (file.path.includes(componentsPath)) {
            // 记录组件被引用情况
            const targetComponentPath = transferFilePathToComponentPath(file.path)

            componentsMap[targetComponentPath].quotes.push(componentPath)
          }

          // 标记组件是否被主页引用
          if (isMainPackagePageByPath) {
            componentsMap[componentPath].referForMainPackage = true
          }
        }
      })
    })
  )
}

/**
 * 分析间接引用依赖
 */
function analyseIndirectDependencies(done) {
  for (const componentPath in componentsMap) {
    const componentInfo = componentsMap[componentPath]

    if (!componentInfo.referForMainPackage) {
      const isIndirectReferComponent = checkIsIndirectReferComponent(componentPath)

      if (isIndirectReferComponent) {
        componentInfo.referForMainPackage = true
      }
    }
  }

  done()
}

/**
 * 是否为被主页间接引用的组件
 */
function checkIsIndirectReferComponent(componentPath) {
  const componentInfo = componentsMap[componentPath]

  if (componentInfo.referForMainPackage) {
    return true
  }

  for (const filePath of componentInfo.refers) {
    if (filePath.includes(componentsPath)) {
      const subComponentPath = transferFilePathToComponentPath(filePath)
      const result = checkIsIndirectReferComponent(subComponentPath)

      if (result) {
        return result
      }
    }
  }
}

/**
 * 将组件路径转换为文件路径
 */
function transferComponentPathToFilePath(componentPath) {
  return path.join(componentsPath, componentPath.replace(componentRootPath, '')) + '.vue'
}

/**
 * 将文件路径转换为组件路径
 */
function transferFilePathToComponentPath(filePath) {
  return filePath
    .replace(componentsPath, componentRootPath)
    .replaceAll(path.sep, '/')
    .replace('.vue', '')
}

/**
 * 判断页面路径是否为主包页面
 */
function checkIsMainPackagePageByPath(filePath) {
  // 从 pages 文件中获取主包页面路径列表

  // 正则：判断是否为主包页面
  const isMainPackagePageReg = new RegExp(`(${mainPackagePagePathList.join('|')})`)

  return isMainPackagePageReg.test(filePath)
}

// 保存依赖分析数据
function saveDependenciesData(done) {
  fs.writeFileSync(dependenciesJsonDistPath, JSON.stringify(componentsMap, null, 4), 'utf8')
  done()
}

// 分发组件
async function distributionComponents() {
  for (let componentPath in componentsMap) {
    const componentInfo = componentsMap[componentPath]

    // 未被主包引用的组件
    for (const pagePath of componentInfo.refers) {
      // 将组件复制到分包
      if (pagePath.includes(pagesPath)) {
        // 将组件复制到页面所在分包
        await copyComponent(componentPath, pagePath)
      }
    }
  }
}

/**
 * 复制组件
 * @param {*} componentPath
 * @param {*} targetPath
 * @returns
 */
async function copyComponent(componentPath, pagePath) {
  const componentInfo = componentsMap[componentPath]

  // 只处理不被主页引用的组件
  if (componentInfo.referForMainPackage) return

  const key = `${componentPath}_${pagePath}`

  // 避免重复任务
  if (taskMap[key]) return

  taskMap[key] = true

  const subPackageRoot = getSubPackageRootByPath(pagePath)

  if (!subPackageRoot) return

  const componentFilePath = transferComponentPathToFilePath(componentPath)
  const subPackageComponentsPath = path.join(subPackageRoot, newComponentDir)
  const newComponentFilePath = path.join(subPackageComponentsPath, path.basename(componentFilePath))
  const newComponentsPath = newComponentFilePath
    .replace(srcPath, '@')
    .replaceAll(path.sep, '/')
    .replaceAll('.vue', '')

  // 1. 复制组件及其资源
  await copyComponentWithResources(componentFilePath, subPackageComponentsPath, componentInfo)

  // 2. 递归复制引用的组件
  if (componentInfo.quotes.length > 0) {
    let tasks = []

    componentInfo.quotes.map(quotePath => {
      // 复制子组件
      tasks.push(copyComponent(quotePath, pagePath))

      const subComponentInfo = componentsMap[quotePath]

      if (!subComponentInfo.referForMainPackage) {
        // 2.1 修改组件引用的子组件路径
        const newSubComponentFilePath = path.join(subPackageComponentsPath, path.basename(quotePath))
        const newSubComponentsPath = newSubComponentFilePath
          .replace(srcPath, '@')
          .replaceAll(path.sep, '/')
          .replaceAll('.vue', '')
        updateChangeFileInfo(newComponentFilePath, quotePath, newSubComponentsPath)
      }
    })
    await Promise.all(tasks)
  }

  // 3. 修改页面引用当前组件路径
  updateChangeFileInfo(pagePath, componentPath, newComponentsPath)

  // 4. 删除当前组件
  updateDeleteFileInfo(componentFilePath)
}

/**
 * 更新删除文件信息
 * @param {*} filePath
 */
function updateDeleteFileInfo(filePath) {
  deleteFileMap[filePath] = true
}

/**
 * 更新修改文件内容信息
 * @param {*} filePath
 * @param {*} oldStr
 * @param {*} newStr
 */
function updateChangeFileInfo(filePath, oldStr, newStr) {
  if (!changeFileMap[filePath]) {
    changeFileMap[filePath] = []
  }
  changeFileMap[filePath].push([oldStr, newStr])
}

/**
 * 删除文件任务
 */
async function deleteFile() {
  for (const filePath in deleteFileMap) {
    try {
      await fs.promises.unlink(filePath).catch(console.log) // 删除单个文件
      // 或删除目录：await fs.rmdir('path/to/dir', { recursive: true });
    } catch (err) {
      console.error('删除失败:', err)
    }
  }
}

/**
 * 复制组件及其资源
 * @param {*} componentFilePath
 * @param {*} destPath
 */
async function copyComponentWithResources(componentFilePath, destPath) {
  // 复制主组件文件
  await new Promise(resolve => {
    src(componentFilePath)
      .pipe(dest(destPath))
      .on('end', resolve)
  })

  // 处理组件中的相对路径资源
  const content = await fs.promises.readFile(componentFilePath, 'utf-8')
  const relativePaths = extractRelativePaths(content)

  await Promise.all(
    relativePaths.map(async relativePath => {
      const resourceSrcPath = path.join(componentFilePath, '../', relativePath)
      const resourceDestPath = path.join(destPath, path.dirname(relativePath))

      await new Promise(resolve => {
        src(resourceSrcPath)
          .pipe(dest(resourceDestPath))
          .on('end', resolve)
      })
    })
  )
}

/**
 * 修改页面引用路径
 */
async function changePageResourcePath() {
  for (const pagePath in changeFileMap) {
    const list = changeFileMap[pagePath]

    await new Promise(resolve => {
      src(pagePath)
        .pipe(
          tap(file => {
            let content = String(file.contents)

            for (const [oldPath, newPath] of list) {
              content = content.replaceAll(oldPath, newPath)
            }
            file.contents = Buffer.from(content)
          })
        )
        .pipe(dest(path.join(pagePath, '../')))
        .on('end', resolve)
    })
  }
}

// 获取分包根目录
function getSubPackageRootByPath(pagePath) {
  for (const subPackagePagePath of subPackagePagePathList) {
    const rootPath = `${path.join(pagesPath, subPackagePagePath)}`
    const arr = pagePath.replace(pagesPath, '').split(path.sep)

    if (arr[1] === subPackagePagePath) {
      return rootPath
    }
  }
}

const tasks = [
  initComponentsMap,
  analyseDependencies,
  analyseIndirectDependencies,
  distributionComponents,
  changePageResourcePath,
  deleteFile,
  saveDependenciesData
]

exports.default = done => {
  initData()
  return series(...tasks)(done)
}
